#ifndef SIMPLE_SUPPORT_ITERATOR_IN_FILTER_HPP
#define SIMPLE_SUPPORT_ITERATOR_IN_FILTER_HPP

#include <iterator> // std::iterator_traits std::bidirectional_iterator_tag
#include <type_traits> // std::common_type_t std::remove_cvref_t
#include <utility> // std::declval
#include "../algorithm/find.hpp" // find_if
#include "../algorithm/traits.hpp" // iterable
#include "../algorithm/reaches.hpp" // reaches sequence
#include "../algorithm/adl_helper.hpp" // adl_helper::adl_begin adl_helper::adl_end

namespace simple::support
{

	struct in_filter_end {};

	template <typename Predicate, sequence Sequence>
	struct in_filter
	{
		using Iterator = std::remove_cvref_t<decltype(adl_helper::adl_begin(std::declval<Sequence>()))>; // hmm...
		using value_type = typename std::iterator_traits<Iterator>::value_type;
		using difference_type = typename std::iterator_traits<Iterator>::difference_type;
		using pointer = typename std::iterator_traits<Iterator>::pointer;
		using reference = typename std::iterator_traits<Iterator>::reference;
		using iterator_category = std::common_type_t<
			std::bidirectional_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category>;

		Predicate predicate;
		Sequence seq;
		Iterator base;

		template <typename P, sequence S>
		constexpr in_filter(P&& predicate, S&& s)
			: predicate(std::forward<P>(predicate)), seq(std::forward<S>(s)),
				base(find_if(adl_helper::adl_begin(seq), adl_helper::adl_end(seq), predicate))
		{}

		constexpr in_filter& operator++()
		{
			++base;
			base = find_if(base, adl_helper::adl_end(seq), predicate);
			return *this;
		}

		// NOTE: wanna make this return auto, but then std say i have to do all the boilerplate myself
		// lose info in case of vuple, but not a big deal in this context i suppose
		[[nodiscard]]
		constexpr bool operator==(in_filter_end) const
		{
			return reaches(base, adl_helper::adl_end(seq));
		}

		[[nodiscard]]
		constexpr decltype(auto) operator*() const
		{
			return *base;
		}

		constexpr auto operator->() const
		{
			return base;
		}

		// TODO: -- if bidir
		// TODO: < if random access
	};

	template <typename P, typename S> in_filter(P&&, S&&) -> in_filter<P, S>;

} // namespace simple::support

#endif /* end of include guard */
